package net.lovesrc.allin.biz.gt;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.github.kevinsawicki.http.HttpRequest;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.zhouzhou.fastjava.BizException;
import com.zhouzhou.fastjava.http.MyHttpRequest;
import com.zhouzhou.fastjava.http.Requests;
import com.zhouzhou.fastjava.util.MoneyKit;
import com.zhouzhou.fastjava.util.StringKit;
import com.zhouzhou.fastjava.util.ThreadKit;
import net.lovesrc.allin.biz.BaseService;
import net.lovesrc.allin.biz.idcard.IdCardService;
import net.lovesrc.allin.biz.jietu.JieTuOuterContext;
import net.lovesrc.allin.biz.jietu.JieTuService;
import net.lovesrc.allin.biz.pay.PayService;
import net.lovesrc.allin.bo.Device;
import net.lovesrc.allin.utils.MyRandomUtils;
import net.lovesrc.allin.utils.TimeUtils;
import net.lovesrc.support.service.dama.DaMaService;
import net.lovesrc.support.service.generator.bankcard.BankCardBinGenerate;
import net.lovesrc.support.service.generator.bankcard.BankCardNumberGenerator;
import net.lovesrc.support.service.generator.bankcard.BankCardTypeEnum;
import net.lovesrc.support.service.generator.bankcard.BankNameEnum;
import net.lovesrc.support.service.mobileverifycode.MobileVerifyCodeService;
import net.lovesrc.support.service.mobileverifycode.MobileVerifyCodeWrapper;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.http.HttpHeaders;
import org.apache.http.client.CookieStore;
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.HttpClientBuilder;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


@Service
public class GtService extends BaseService {

    private static final String CheckTokenUrl = "http://app.gtnu.org/App/Api/CheckToken";

    @Autowired
    private MobileVerifyCodeService mobileVerifyCodeService;

    @Autowired
    private IdCardService idCardService;

    @Autowired
    private DaMaService daMaService;

    @Autowired
    private JieTuService jieTuService;

    @Autowired
    private PayService payService;

    /**
     * 匹配成功未付款
     */

    private MobileVerifyCodeWrapper getMobileVerifyCodeWrapper() {
        return new MobileVerifyCodeWrapper(mobileVerifyCodeService, "35778");
    }

    public String newRegister(String registerPromotionCode) {

        if (StringUtils.isBlank(registerPromotionCode)) {
            throw BizException.messageOf("registerPromotionCode can't be null");
        }
        CookieStore cookieStore = new BasicCookieStore();
        HttpClient httpClient = newHttpClientWithMyProxy(null, cookieStore);

        String homeUrl = "http://app.gtnu.org/www/index.html";
        MyHttpRequest.get(httpClient, homeUrl)
                .executeUtilReturn200()
                .asText();
        byte[] imageBytes = MyHttpRequest.get(httpClient, "http://app.gtnu.org/Common/ValidateCode")
                .header("Referer", "http://app.gtnu.org/www/index.html")
                .param("_", System.currentTimeMillis())
                .executeUtilReturn200()
                .asBytes();

        String verifyCode = daMaService.recognise("4004", "image/jpeg", new ByteArrayInputStream(imageBytes));

        String mobile = doGetNewMobile();

        // 超时也是有可能发送短信成功的
        JSONObject sendCodeResJson = null;
        try {
            sendCodeResJson = MyHttpRequest.post(httpClient, "http://app.gtnu.org/App/Api/SendCode?t=0&type=0&country=41")
                    .param("number", mobile)
                    .param("Code", verifyCode)
                    .execute()
                    .asJson();
        } catch (Exception e) {
            logger.error("SendCode Error, Mobile: {}", mobile, e);
        }
        try {
            if (sendCodeResJson != null) {
                checkResJson(sendCodeResJson);
            }
        } catch (BizException e) {
            if (e.getMessage().contains("手机号码已注册")) {
                getMobileVerifyCodeWrapper().addBlack(mobile);
            } else {
                getMobileVerifyCodeWrapper().releasePhone(mobile);
            }
            throw e;
        }
        String smsVerifyCode = getMobileVerifyCodeWrapper().getVerifyCodeFromPhone(mobile);

        // 用户名必须包含字母、数字,且长度要在6-16位之间
        String username = randomAlphanumeric(6, 10).toLowerCase();
        // 登录密码必须包含字母、数字,且长度要在8-16位之间
        String password = randomAlphanumeric(8, 12);
        String payPassword = RandomStringUtils.randomNumeric(6);

        JSONObject resJson = MyHttpRequest.post(httpClient, "http://app.gtnu.org/App/Api/Register")
                .param("Ticket", StringUtils.EMPTY)
                .param("LoginName", username)
                .param("CountryID", 41)
                .param("Phone", mobile)
                .param("CodeT", verifyCode)
                .param("Code", smsVerifyCode)
                .param("LoginPwd", password)
                .param("LoginPwdRep", password)
                .param("LoginPwd2", payPassword)
                .param("LoginPwd2Rep", payPassword)
                .param("RegName", registerPromotionCode)
                .execute()
                .asJson();
        checkResJson(resJson);

        JSONObject promotionAccount = queryForRow(jdbcTemplate, "select * from t_gt_account where username = ?", registerPromotionCode);
        int newLvl = promotionAccount != null ? promotionAccount.getIntValue("lvl") + 1 : 1;

        JSONObject newRow = new JSONObject()
                .fluentPut("mobile", mobile)
                .fluentPut("registerPromotionCode", registerPromotionCode)
                .fluentPut("username", username)
                .fluentPut("password", password)
                .fluentPut("payPassword", payPassword)
                .fluentPut("registerTime", new Date())
                .fluentPut("lvl", newLvl);
        insertRow(jdbcTemplate, "t_gt_account", newRow);
        return mobile;
    }

    /**
     * 确保有字母和数字
     */
    private String randomAlphanumeric(final int minLengthInclusive, final int maxLengthExclusive) {
        for (; ; ) {
            String chars = RandomStringUtils.randomAlphanumeric(minLengthInclusive, maxLengthExclusive);
            if (Pattern.compile("\\d+").matcher(chars).find()
                    && Pattern.compile("[a-zA-Z]+").matcher(chars).find()
            ) {
                return chars;
            }
        }
    }

    public void authId(String mobile) {
        try {
            this.doAuthId(mobile);
        } catch (Exception e) {
            logger.error("auth id error, {}", mobile, e);
            jdbcTemplate.update("update t_gt_account set idStatus = 'failure', idTime = ?, idMessage = ? where mobile = ?", new Date(), ExceptionUtils.getMessage(e), mobile);
        }
    }

    public synchronized void doAuthId(String mobile) {
        // 登录
        JSONObject row = queryForRow(jdbcTemplate, "select * from t_gt_account where mobile = ?", mobile);
        String idStatus = row.getString("idStatus");
        if (StringUtils.isNotBlank(idStatus)) {
            return;
        }
        String payPassword = row.getString("payPassword");
        /*
        if (RandomUtils.nextInt(0, 6) == 5) {
            JSONObject sets = new JSONObject()
                    .fluentPut("idStatus", "do-not-id-auth")
                    .fluentPut("idTime", new Date())
                    .fluentPut("idMessage", "不需要实名");
            updateRow(jdbcTemplate, "t_gt_account", sets, "mobile = ?", mobile);
            return;
        }
        */
        if (StringUtils.isBlank(row.getString("cookies"))) {
            doLogin(mobile);
        }
        // 实名
        JSONObject idRow = getFreeIdCard();
        String realName = idRow.getString("realName");
        String idCardNumber = idRow.getString("idCardNumber");
        JSONObject resJson = this.post(mobile, "http://app.gtnu.org/App/Api/Info",
                new JSONObject()
                        .fluentPut("iptName", row.getString("username"))
                        .fluentPut("FullName", realName)
                        .fluentPut("IDCard", idCardNumber)
                        .fluentPut("iptCountry", "China中国")
                        .fluentPut("iptPhone", mobile)
                        .fluentPut("Pwd", payPassword)
        );
        JSONObject sets = new JSONObject()
                .fluentPut("idCardNumber", idCardNumber)
                .fluentPut("realName", realName)
                .fluentPut("idStatus", "submitted")
                .fluentPut("idTime", new Date())
                .fluentPut("idMessage", resJson.getString("Message"))
                .fluentPut("idResText", resJson.toJSONString());
        updateRow(jdbcTemplate, "t_gt_account", sets, "mobile = ?", mobile);
    }

    private JSONObject getFreeIdCard() {
        String sql = "select * from t_id_card where worked = 1 and frontOcrStatus = 'normal' and backOcrStatus = 'normal' " +
                " and idCardNumber not in (select idCardNumber from t_gt_account where idCardNumber is not null) " +
                " and idCardNumber not in (select idCardNumber from t_gt_used_id) " +
                " order by rand()" +
                " limit 1";
        JSONObject row = queryForRow(jdbcTemplate, sql);
        if (row == null) {
            throw BizException.messageOf("无可用的身份证信息了");
        }
        return row;
    }

    private String doLogin(String mobile) {
        JSONObject row = queryForRow(jdbcTemplate, "select * from t_gt_account where mobile = ?", mobile);
        CookieStore cookieStore = getCookieStore(mobile);
        HttpClient httpClient = newHttpClientWithMyProxy(mobile, cookieStore);
        JSONObject loginResJson = MyHttpRequest.get(httpClient, "http://app.gtnu.org/App/Api/Login")
                .param("name", mobile)
                .param("pwd", row.getString("password"))
                .executeUtilReturn200()
                .asJson();
        checkResJson(loginResJson);
        String token = loginResJson.getString("Message");
        if (StringUtils.isBlank(token)) {
            throw BizException.messageOf("{} 登陆失败，未能获取到token", mobile);
        }
        JSONObject sets = new JSONObject()
                .fluentPut("token", token)
                .fluentPut("cookies", Requests.toCookieString(cookieStore))
                .fluentPut("updateTime", new Date());
        updateRow(jdbcTemplate, "t_gt_account", sets, "mobile = ?", mobile);
        return token;
    }

    private String doGetNewMobile() {
        for (; ; ) {
            String mobile = getMobileVerifyCodeWrapper().getPhone();
            if (jdbcTemplate.queryForObject("select count(1) from t_gt_account where mobile = ?", long.class, mobile) > 0) {
                getMobileVerifyCodeWrapper().addBlack(mobile);
                continue;
            }
            return mobile;
        }
    }

    private boolean isSuccess(JSONObject resJson) {
        return resJson.getBooleanValue("Active");
    }

    private void checkResJson(JSONObject resJson) {
        if (isSuccess(resJson)) {
            // is okay
        } else {
            throw BizException.messageOf(resJson.getString("Message"));
        }
    }

    public void syncStatus(String mobile) {
        try {
            this.doSyncStatus(mobile);
        } catch (Exception e) {
            logger.error("sync auth id error, {}", mobile, e);
            jdbcTemplate.update("update t_gt_account set idStatus = 'sync-failure', idTime = ?, idMessage = ? where mobile = ?", new Date(), ExceptionUtils.getMessage(e), mobile);
        }
    }

    private JSONObject doCheckToken(String mobile) {
        return this.get(mobile, CheckTokenUrl,
                new JSONObject().fluentPut("_", System.currentTimeMillis())
        );
    }

    private JSONObject doGetUserInfoJson(String mobile) {
        JSONObject resJson = doCheckToken(mobile);
        safeRun(() -> {
            JSONObject sets = new JSONObject()
                    .fluentPut("ye1", resJson.getString("Ye1")) // 可交易
                    .fluentPut("ye2", resJson.getString("Ye2")) // 签到释放
                    .fluentPut("ye3", resJson.getString("Ye3")) // 直推已购币
                    .fluentPut("ye4", resJson.getString("Ye4")) // 矿池资产
                    .fluentPut("ye5", resJson.getString("Ye5")) // 矿池收益
                    .fluentPut("ye6", resJson.getString("Ye6")) // 交易流量
                    .fluentPut("ye7", resJson.getString("Ye7")) // 个人已购币,可买矿机
                    .fluentPut("ye8", resJson.getString("Ye8")) // 矿机收益
                    .fluentPut("ye9", resJson.getString("Ye9"))
                    .fluentPut("ye10", resJson.getString("Ye10"))
                    .fluentPut("payActive", resJson.getBooleanValue("PayActive") ? 1 : 0);
            updateRow(jdbcTemplate, "t_gt_account", sets, "mobile = ?", mobile);
        });
        return resJson;
    }

    public GtUser getUserInfo(String mobile) {
        return this.getUserInfo(mobile, false);
    }

    /**
     * @param rede 是否自动复投
     */
    public GtUser getUserInfo(String mobile, boolean rede) {
        JSONObject resJson = doGetUserInfoJson(mobile);
        GtUser gtUser = resJson.toJavaObject(GtUser.class);
        if (rede) {
            boolean hasRede = false;
            if (gtUser.getYe8().intValue() >= 10) {
                this.rede(mobile, "Type0", (gtUser.getYe8().intValue() / 10) * 10);
                hasRede = true;
            }
            if (gtUser.getYe7().intValue() >= 10) {
                this.rede(mobile, "Type3", (gtUser.getYe7().intValue() / 10) * 10);
                hasRede = true;
            }
            if (hasRede) {
                resJson = doGetUserInfoJson(mobile);
                gtUser = resJson.toJavaObject(GtUser.class);
            }
        }
        return gtUser;
    }

    private void doSyncStatus(String mobile) {
        JSONObject row = queryForRow(jdbcTemplate, "select * from t_gt_account where mobile = ?", mobile);
        String idStatus = row.getString("idStatus");
        if (StringKit.notEqualsIgnoreCase("submitted", idStatus)) {
            return;
        }
        GtUser gtUser = getUserInfo(mobile);
        String authenState = gtUser.getAuthenState();
        if ("认证通过".equalsIgnoreCase(authenState)) {
            jdbcTemplate.update("update t_gt_account set idStatus = 'success', idSuccessTime = ?, payMethod = ? where mobile = ?", new Date(), MyRandomUtils.randomPayMethod(), mobile);
            onAuthIdSuccess(mobile);
        } else if ("审核中".equalsIgnoreCase(authenState)) {
            // nothing to do
        } else {
            throw BizException.messageOf(authenState);
        }
    }

    public void signIn(String mobile) {

        JSONObject resJson = this.get(mobile, "http://app.gtnu.org/App/Api/QianDao?act=qiandao");
        String message = resJson.getString("Message");
        if (isSuccess(resJson) || message.contains("已签到")) {
            // success
            jdbcTemplate.update("update t_gt_account set signInTime = ? where mobile = ?", new Date(), mobile);
        } else if (message.startsWith("距离下次签到还有")) {
            Pattern pattern = Pattern.compile("距离下次签到还有(\\d+)天(\\d+)小时(\\d+)分");
            Matcher matcher = pattern.matcher(message);
            if (matcher.find()) {
                //int days = Integer.valueOf(matcher.group(1));
                int hours = Integer.valueOf(matcher.group(2));
                jdbcTemplate.update("update t_gt_account set nextSignInTime = nextSignInTime + interval ? hour where mobile = ?", hours + 1, mobile);
            } else {
                throw BizException.messageOf(message);
            }
        } else {
            throw BizException.messageOf(message);
        }
    }

    public void syncUserInfo(String mobile) {
        GtUser gtUser = this.getUserInfo(mobile, true);
        if (gtUser.getYe6().intValue() >= 7
                || gtUser.getYe1().intValue() >= 13
                || RandomUtils.nextInt(0, 3) == 0) {
            this.tryResetPayAccount(mobile);
        }
        if (isMyMobile(mobile)) {
            this.syncMyAccountOrders(mobile);
        }
        jdbcTemplate.update("update t_gt_account set syncUserInfoTime = ? where mobile = ?", new Date(), mobile);
    }

    public void profit(String mobile) {
        JSONObject resJson = this.get(mobile, "http://app.gtnu.org/App/Api/Host?act=shouyis");
        String message = resJson.getString("Message");
        if (isSuccess(resJson)
                || message.contains("已领取")
                || message.contains("领取成功")) {
            // success
        } else {
            throw BizException.messageOf(message);
        }
        jdbcTemplate.update("update t_gt_account set profitTime = ? where mobile = ?", new Date(), mobile);
    }

    public void newSysJy(String owner) {
        checkOwner(owner);
        JSONObject sellerRow = queryForRow(jdbcTemplate, "select * from v_gt_other_account " +
                " where mobile not in (select mobile from v_gt_can_not_sell_now) " +
                " and ye1 > 13 and ye6 >= 10" +
                " order by updateTime asc limit 1");
        if (sellerRow == null) {
            return;
        }
        String sellerMobile = sellerRow.getString("mobile");
        String sellerRoot = sellerRow.getString("root");
        String buyerMobile = this.newSysJyBuyer(sellerMobile, sellerRoot);
        if (StringUtils.isBlank(buyerMobile)) {
            return;
        }
        this.newJy(owner, "SysJy", buyerMobile, sellerMobile);
    }

    private Integer calcSellerMaxJyQty(GtUser gtUser) {
        return uniformQty(
                Math.min(
                        Double.valueOf(gtUser.getYe1().doubleValue() / 1.3).intValue(),
                        gtUser.getYe6().intValue()
                )
        );
    }


    /**
     * 之前有过交易的，就不让再交易了
     */
    private synchronized String newSysJyBuyer(String sellerMobile, String sellerRoot) {
        String baseSql = "select * from v_gt_other_account" +
                " where mobile not in (select buyerMobile from t_gt_jy where buyerMobile is not null and insertTime > date(now()))" +
                " and mobile not in (select mobile from v_gt_can_not_sell_now)" +
                " and mobile not in (select buyerMobile from t_gt_jy where sellerMobile = ? and buyerMobile is not null)" +
                " and mobile not in (select sellerMobile from t_gt_jy where buyerMobile = ? and sellerMobile is not null)" +
                " and mobile <> ?" +
                " and root <> ?";
        JSONObject row = queryForRow(jdbcTemplate, baseSql +
                " and ye1 >= 13" +
                " and ye6 < 10" +
                " order by ye1 desc, rand() " +
                " limit 1", sellerMobile, sellerMobile, sellerMobile, sellerRoot);
        return row == null ? null : row.getString("mobile");
    }

    public void newToMyJyWithAutoSelectSeller(String owner, String type, String buyerMobile) {
        JSONObject buyer = queryForRow(jdbcTemplate, "select * from v_gt_account where mobile = ?", buyerMobile);
        String buyerRoot = buyer.getString("root");
        JSONObject seller = queryForRow(jdbcTemplate, "select * from v_gt_other_account " +
                        " where ye1 >= 13 " +
                        " and ye6 >= 10 " +
                        " and root <> ? " +
                        " and mobile not in (select mobile from v_gt_can_not_sell_now)" +
                        " and mobile not in (select mobile from v_gt_jy_ing_mobile)" +
                        " order by least(ye1 / 1.3, ye6) desc limit 1",
                buyerRoot);
        if (seller == null) {
            throw BizException.messageOf("无卖出小号了");
        }
        this.newJy(owner, type, buyerMobile, seller.getString("mobile"));
    }

    public synchronized void newJy(String owner, String type, String buyerMobile, String sellerMobile) {

        checkOwner(owner);
        checkIsMobile(buyerMobile);
        checkIsMobile(sellerMobile);

        if (isNotJyTime()) {
            throw BizException.messageOf("当前不在交易时间内");
        }
        if (buyerMobile.equals(sellerMobile)) {
            throw BizException.messageOf("卖出和买入的账号不能一致");
        }
        if (jdbcTemplate.queryForObject("select count(1) from v_gt_can_not_sell_now where mobile = ?", int.class, sellerMobile) > 0) {
            throw BizException.messageOf("{} 今日已有卖出订单，无法再卖出了", sellerMobile);
        }
        this.resetPayAccount(sellerMobile);
        this.resetPayAccount(buyerMobile);

        GtUser sellerGtUser = this.getUserInfo(sellerMobile);
        if (!sellerGtUser.isPayActive()) {
            throw BizException.messageOf("用户{}被冻结了", sellerMobile);
        }
        GtUser buyerGtUser = this.getUserInfo(buyerMobile);
        if (!buyerGtUser.isPayActive()) {
            throw BizException.messageOf("用户{}被冻结了", buyerMobile);
        }
        Integer jyQty = calcSellerMaxJyQty(sellerGtUser);
        GtPrice gtPrice = getPrice(sellerMobile, jyQty);
        this.publishBuyOrder(buyerMobile, gtPrice, jyQty);
        GtPreOrder order = this.getMyPreBuyOrders(buyerMobile).stream().findFirst().get();
        Integer jyId = order.getId();
        try {
            JSONObject newRow = new JSONObject()
                    .fluentPut("jyId", jyId)
                    .fluentPut("status", "New")
                    .fluentPut("buyerMobile", buyerMobile)
                    .fluentPut("sellerMobile", sellerMobile)
                    .fluentPut("jyQty", jyQty)
                    .fluentPut("jyAmount", order.getSaleMoney())
                    .fluentPut("owner", owner)
                    .fluentPut("type", type);
            insertRow(jdbcTemplate, "t_gt_jy", newRow);
        } catch (Exception e) {
            this.cancelOrder(buyerMobile, jyId);
            throw e;
        }
    }

    public void buyJy(String sellerMobile, Integer jyId) {
        JSONObject seller = queryForRow(jdbcTemplate, "select * from t_gt_account where mobile = ?", sellerMobile);
        bidJy(seller, jyId);
        GtOrder topGtOrder = getMyIngTopSellOrder(sellerMobile);
        if (topGtOrder == null) {
            throw BizException.messageOf(sellerMobile + " 的卖出订单，未获取到数据");
        }
        Integer orderId = topGtOrder.getId();
        GtOrderDetail gtOrderDetail = this.getOrderDetailAsSeller(sellerMobile, orderId);
        JSONObject sets = new JSONObject()
                .fluentPut("orderId", orderId)
                .fluentPut("status", "Waiting_Buyer_Pay")
                .fluentPut("sellerMobile", sellerMobile)
                .fluentPut("orderAmount", gtOrderDetail.getObj().getSaleMoney())
                .fluentPut("orderQty", gtOrderDetail.getObj().getMoney());
        updateRow(jdbcTemplate, "t_gt_jy", sets, "jyId = ?", jyId);

        safeRun(() -> {
            Integer jyQty = jdbcTemplate.queryForObject("select jyQty from t_gt_jy where jyId = ?", Integer.class, jyId);
            if (!gtOrderDetail.getObj().getMoney().equals(jyQty)) {
                notificationService.syncEmail(jyId + "交易的数量与订单的数量，不一致，请检查！！！");
            }
        });
    }

    private Integer uniformQty(double qty) {
        Integer maxJyQty = tryGetProperties(int.class, 100, "Gt_Max_Jy_Qty");
        if (qty > maxJyQty) {
            qty = maxJyQty;
        }
        Integer uniformQty;
        if (qty >= 50) {
            uniformQty = 50;
        } else if (qty >= 20) {
            uniformQty = 20;
        } else if (qty >= 10) {
            uniformQty = 10;
        } else {
            throw BizException.messageOf("计算交易数据错误");
        }
        return uniformQty;
    }

    private void onAuthIdSuccess(String mobile) {
        this.signIn(mobile);
        this.setRoots();

        // 设置手机设备
        jdbcTemplate.update("update t_gt_account set device = ? where mobile = ? and device is null", MyRandomUtils.randomDevice().name(), mobile);
    }

    public List<GtOrder> getMyIngBuyOrders(String mobile) {
        JSONObject resJson = this.get(mobile, "http://app.gtnu.org/App/Api/TradeItem?act=mybuylistitem&leftIndex=1");
        checkResJson(resJson);
        return resJson.getJSONArray("Obj").toJavaList(GtOrder.class);
    }

    public GtOrder getMyIngTopSellOrder(String mobile) {
        for (int i = 0; i < 10; i++) {
            List<GtOrder> myIngSellOrders = getMyIngSellOrders(mobile);
            if (myIngSellOrders != null && myIngSellOrders.size() > 0) {
                return myIngSellOrders.get(0);
            }
            ThreadKit.sleepSeconds(1);
        }
        return null;
    }

    public List<GtOrder> getMyIngSellOrders(String mobile) {
        JSONObject resJson = this.get(mobile, "http://app.gtnu.org/App/Api/TradeItem?act=mysalelistitem&leftIndex=1");
        checkResJson(resJson);
        return resJson.getJSONArray("Obj").toJavaList(GtOrder.class);
    }

    public List<GtPreOrder> getMyPreBuyOrders(String mobile) {
        JSONObject resJson = this.get(mobile, "http://app.gtnu.org/App/Api/Trade?act=mylist&leftIndex=1");
        checkResJson(resJson);
        return resJson.getJSONArray("Obj").toJavaList(GtPreOrder.class);
    }

    public List<GtPreOrder> getPreBuyOrders(String mobile) {
        JSONObject resJson = this.get(mobile, "http://app.gtnu.org/App/Api/Trade?act=buylist&leftIndex=1");
        checkResJson(resJson);
        return resJson.getJSONArray("Obj").toJavaList(GtPreOrder.class);
    }

    /**
     * 历史订单
     */
    public List<GtOrder> getMyOrders(String mobile) {
        JSONObject resJson = this.get(mobile, "http://app.gtnu.org/App/Api/TradeItem?act=mylistitem&type=1&leftIndex=1");
        checkResJson(resJson);
        return resJson.getJSONArray("Obj").toJavaList(GtOrder.class);
    }

    public int getPreBuyVolumn(String mobile) {
        JSONObject resJson = this.get(mobile, "http://app.gtnu.org/App/Api/Trade?act=buylist&leftIndex=1");
        checkResJson(resJson);
        return resJson.getBigDecimal("Volume").intValue();
    }

    public File jieTu(Integer jyId, boolean refresh) {
        File imgFile = new File(JieTuService.JIE_TU_DIR, "gt-jy-" + jyId + ".png");
        if (!imgFile.exists() || refresh) {
            return newJieTu(jyId);
        }
        return imgFile;
    }

    private File newJieTu(Integer jyId) {
        File file = jieTuService.newJieTu("http://localhost:8080/jietu/gt/alipay?jyId=" + jyId, "gt-jy-" + jyId);
        try {
            FileUtils.copyFileToDirectory(file, JieTuService.JIE_TU_DIR);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return new File(JieTuService.JIE_TU_DIR, "gt-jy-" + jyId + ".png");
    }

    public void payJyWithAutoJieTu(Integer jyId) {
        File imgFile = this.jieTu(jyId, false);
        this.payJy(jyId, imgFile);
        return;
    }

    public void payJy(Integer jyId, File img) {
        JSONObject jyRow = queryForRow(jdbcTemplate, "select * from t_gt_jy where jyId = ?", jyId);
        String buyerMobile = jyRow.getString("buyerMobile");
        String status = jyRow.getString("status");
        if (StringKit.notEqualsIgnoreCase("Waiting_Buyer_Pay", status)) {
            throw BizException.messageOf("状态异常");
        }
        this.getUserInfo(buyerMobile);
        Integer orderId = getJyOrderIdByJyId(jyId);
        for (; ; ) {
            try {
                if (this.getOrderDetailAsBuyer(buyerMobile, orderId).getConfirmState().equalsIgnoreCase("已付款等待收款")) {
                    break;
                }
                JSONObject row = queryForRow(jdbcTemplate, "select * from t_gt_account where mobile = ?", buyerMobile);
                if (row.getIntValue("tryPayTimes") >= 10) {
                    throw BizException.messageOf("重试支付次数超过最大次数, {}", jyId);
                }
                JSONArray cookiesArray = JSON.parseArray(row.getString("cookies"));
                HttpRequest httpRequest = HttpRequest.post("http://app.gtnu.org/App/Api/TradeItem?act=upload&token=" + row.getString("token"));
                List<String> cookies = Lists.newArrayList();
                for (int i = 0; i < cookiesArray.size(); i++) {
                    JSONObject cookieJson = cookiesArray.getJSONObject(i);
                    cookies.add(cookieJson.getString("name") + "=" + cookieJson.getString("value"));
                }

                httpRequest.useProxy("forward.xdaili.cn", 80)
                        .userAgent("Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.0 Mobile/14E304 Safari/602.1")
                        .header(HttpHeaders.PROXY_AUTHORIZATION, getMyXDaiLiProxyAuthorization())
                        .header("Cookie", StringUtils.join(cookies, "; "));

                String resText = httpRequest.part("ID", getJyOrderIdByJyId(jyId))
                        .part("Remark", UUID.randomUUID().toString() + ".png", img)
                        .body();
                logger.info("上传交易截图，返回: {}", resText);
                JSONObject resJson = JSON.parseObject(resText);
                checkResJson(resJson);
                break;
            } catch (Exception e) {
                logger.error("Pay Jy Error", jyId, e);
                jdbcTemplate.update("update t_gt_jy set tryPayTimes = tryPayTimes + 1 where buyerMobile = ? and jyId = ?", buyerMobile, jyId);
                String message = e.getMessage();
                if (e instanceof HttpRequest.HttpRequestException) {
                    continue;
                } else if (message.contains("Gateway")
                        || message.contains("Runtime")
                        || message.contains("网络连接失败")) {
                    // 重新生成截图
                    this.jieTu(jyId, true);
                } else {
                    throw e;
                }
            }
        }
        jdbcTemplate.update("update t_gt_jy set status = 'Waiting_Seller_Confirm' where buyerMobile = ? and jyId = ?", buyerMobile, jyId);
    }

    public void cancelOrder(String buyerMobile, Integer orderId) {
        JSONObject resJson = this.get(buyerMobile, "http://app.gtnu.org/App/Api/Trade?act=del&id=" + orderId);
        checkResJson(resJson);
        jdbcTemplate.update("update t_gt_jy set status = 'Cancelled' where jyId = ?", orderId);
    }

    public synchronized void confirmJY(Integer jyId) {
        checkNotNull("jyId", jyId);
        JSONObject jyRow = queryForRow(jdbcTemplate, "select * from t_gt_jy where jyId = ?", jyId);
        String status = jyRow.getString("status");
        if (StringKit.notEqualsIgnoreCase("Waiting_Seller_Confirm", status)) {
            throw BizException.messageOf("状态异常");
        }
        String sellerMobile = jyRow.getString("sellerMobile");
        String buyerMobile = jyRow.getString("buyerMobile");
        Integer orderId = jyRow.getInteger("orderId");
        doConfirmJy(sellerMobile, orderId);
        jdbcTemplate.update("update t_gt_jy set status = 'Success' where jyId = ?", jyId);
        safeRun(() -> {
            this.getUserInfo(sellerMobile, true);
            this.getUserInfo(buyerMobile, true);
        });
    }

    private void doConfirmJy(String sellerMobile, Integer orderId) {
        logger.info("确认交易, SellerMobile: {}, OrderId: {}", sellerMobile, orderId);
        this.get(sellerMobile, "http://app.gtnu.org/App/Api/TradeItem?act=confirm&id=" + orderId);
        GtOrderDetail gtOrderDetail = this.getOrderDetailAsSeller(sellerMobile, orderId);
        if ("已确认收款".equalsIgnoreCase(gtOrderDetail.getConfirmState())) {
            return;
        }
        throw BizException.messageOf("确认交易失败, SellerMobile: {}, OrderId: {}", sellerMobile, orderId);
    }

    private Integer getJyOrderIdByJyId(Integer jyId) {
        return jdbcTemplate.queryForObject("select orderId from t_gt_jy where jyId = ?", int.class, jyId);
    }

    private Integer getBankInfoId(String mobile) {
        JSONObject resJson = this.get(mobile, "http://app.gtnu.org/App/Api/Bank?act=list&leftIndex=1");
        checkResJson(resJson);
        JSONArray objs = resJson.getJSONArray("Obj");
        if (objs == null || objs.isEmpty()) {
            return 0;
        }
        return objs.getJSONObject(0).getIntValue("ID");
    }

    // 收款信息

    /**
     * 支付宝账号必须和注册手机号一致
     * <p>
     * 现在需要获取手机验证码，暂时关闭此逻辑
     */
    public void submitBankInfo(String gtMobile) {

        this.setRoots();

        JSONObject row = queryForRow(jdbcTemplate, "select * from t_gt_account where mobile = ? and isMyAccount = 0", gtMobile);
        String idStatus = row.getString("idStatus");
        if (StringKit.notEqualsIgnoreCase(idStatus, "success")) {
            throw BizException.messageOf("未实名成功的用户，不能提交银行资料");
        }

        String verifyCode = StringUtils.EMPTY;
        String smsVerifyCode = StringUtils.EMPTY;

        HttpClient newHttpClient = newHttpClientWithMyProxy(null, new BasicCookieStore());
        JSONObject cfg = MyHttpRequest.get(newHttpClient, "http://app.gtnu.org/App/Api/Data?act=Cfg")
                .param("_", System.currentTimeMillis())
                .executeUtilReturn200()
                .asJson();
        if (cfg.getBooleanValue("InfoActive")) {
            getMobileVerifyCodeWrapper().getPhone(gtMobile);
            try {
                doGetUserInfoJson(gtMobile);
                // 获取图片验证码
                byte[] imageBytes = newMyHttpRequest(gtMobile, "http://app.gtnu.org/Common/ValidateCode", "GET")
                        .header("Referer", "http://app.gtnu.org/www/index.html")
                        .param("_", System.currentTimeMillis())
                        .executeUtilReturn200()
                        .asBytes();
                verifyCode = daMaService.recognise("4004", "image/jpeg", new ByteArrayInputStream(imageBytes));
                JSONObject sendCodeResJson = this.get(gtMobile, "http://app.gtnu.org/App/Api/SendCode1?type=3&Code=" + verifyCode);
                checkResJson(sendCodeResJson);
            } catch (Exception e) {
                getMobileVerifyCodeWrapper().releasePhone(gtMobile);
                throw e;
            }
            smsVerifyCode = getMobileVerifyCodeWrapper().getVerifyCodeFromPhone(gtMobile);
        }

        File alipayImgFile = payService.createAlipayQrCode(gtMobile);
        File wechatImgFile = payService.createWechatQrCode(gtMobile);

        Integer bankInfoId = getBankInfoId(gtMobile);

        String bankName;
        String bankCode;
        String bankAddress;
        // 数据库已经有了
        if (StringUtils.isNotBlank(row.getString("bankCode"))) {
            bankName = row.getString("bankName");
            bankCode = row.getString("bankCode");
            bankAddress = row.getString("bankAddress");
        } else {
            BankCardBinGenerate.BankCardBin bankCardBin = generateCardBin();
            bankName = bankCardBin.getTitle();
            bankCode = BankCardNumberGenerator.generateByBankCardBin(bankCardBin);
            bankAddress = jdbcTemplate.queryForObject("select address from t_icbc_bank_item where length(address) <= 30 order by rand() desc limit 1", String.class);
        }

        row = queryForRow(jdbcTemplate, "select * from t_gt_account where mobile = ? and isMyAccount = 0", gtMobile);
        JSONArray cookiesArray = JSON.parseArray(row.getString("cookies"));
        List<String> cookies = Lists.newArrayList();
        for (int i = 0; i < cookiesArray.size(); i++) {
            JSONObject cookieJson = cookiesArray.getJSONObject(i);
            cookies.add(cookieJson.getString("name") + "=" + cookieJson.getString("value"));
        }
        RuntimeException ex = null;
        for (int i = 0; i < 5; i++) {
            try {
                HttpRequest httpRequest = HttpRequest.post("http://app.gtnu.org/App/Api/Bank?act=submit&token=" + row.getString("token"));
                httpRequest.useProxy("forward.xdaili.cn", 80)
                        .userAgent("Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.0 Mobile/14E304 Safari/602.1")
                        .connectTimeout(10 * 1000)
                        .readTimeout(10 * 1000)
                        .header(HttpHeaders.PROXY_AUTHORIZATION, getMyXDaiLiProxyAuthorization())
                        .header("Cookie", StringUtils.join(cookies, "; "));

                String resText = httpRequest.part("ID", bankInfoId)
                        .part("Name", bankName)
                        .part("Code", bankCode)
                        .part("FullName", row.getString("realName"))
                        .part("Address", bankAddress)
                        .part("WeiXin", gtMobile)
                        .part("WeiXinImg", UUID.randomUUID().toString() + "." + FilenameUtils.getExtension(wechatImgFile.getName()), wechatImgFile)
                        .part("Alipay", gtMobile)
                        .part("AlipayImg", UUID.randomUUID().toString() + "." + FilenameUtils.getExtension(alipayImgFile.getName()), alipayImgFile)
                        .part("CodeT", verifyCode)
                        .part("Code2", smsVerifyCode)
                        .body();
                logger.info("上传银行卡信息，返回: {}", resText);
                JSONObject resJson = JSON.parseObject(resText);
                checkResJson(resJson);
                JSONObject sets = new JSONObject()
                        .fluentPut("bankName", bankName)
                        .fluentPut("bankCode", bankCode)
                        .fluentPut("bankAddress", bankAddress)
                        .fluentPut("wechat", gtMobile)
                        .fluentPut("alipay", gtMobile);
                updateRow(jdbcTemplate, "t_gt_account", sets, "mobile = ?", gtMobile);
                return;
            } catch (JSONException e) {
                ex = e;
                continue;
            } catch (HttpRequest.HttpRequestException e) {
                ex = e;
                continue;
            }
        }
        if (ex != null) {
            throw ex;
        }
    }

    private static final List<BankNameEnum> BANK_NAME_ENUMS = Lists.newArrayList(
            BankNameEnum.ICBC,
            BankNameEnum.ABC,
            BankNameEnum.CMB,
            BankNameEnum.CCB
    );

    private BankCardBinGenerate.BankCardBin generateCardBin() {
        BankNameEnum bankNameEnum = BANK_NAME_ENUMS.get(RandomUtils.nextInt(0, BANK_NAME_ENUMS.size()));
        BankCardBinGenerate.BankCardBin bankCardBin = BankCardBinGenerate.generate(bankNameEnum, BankCardTypeEnum.DEBIT);
        return bankCardBin;
    }

    /**
     * 匹配成功未付款
     * 已付款等待收款
     * 已确认收款
     */
    public GtOrderDetail getOrderDetailAsBuyer(String buyerMobile, Integer orderId) {
        JSONObject resJson = this.get(buyerMobile, "http://app.gtnu.org/App/Api/TradeItem?act=buydetail&id=" + orderId);
        checkResJson(resJson);
        return resJson.toJavaObject(GtOrderDetail.class);
    }

    public GtOrderDetail getOrderDetailAsSeller(String sellerMobile, Integer orderId) {
        JSONObject resJson = this.get(sellerMobile, "http://app.gtnu.org/App/Api/TradeItem?act=saledetail&id=" + orderId);
        checkResJson(resJson);
        return resJson.toJavaObject(GtOrderDetail.class);
    }

    /**
     * Type0, 矿机收益复投
     * Type1, 可交易GT复投
     * Type3, 个人已购币复投
     */
    private void rede(String mobile, String type, int money) {
        this.get(
                mobile, "http://app.gtnu.org/App/Api/Rede?act=sumbit",
                new JSONObject()
                        .fluentPut("type", type)
                        .fluentPut("money", money)
        );
    }

    private GtPrice getPrice(String mobile, Integer jyQty) {
        // rand mobile
        List<GtPreOrder> preBuyOrders = this.getPreBuyOrders(mobile);
        if (preBuyOrders == null || preBuyOrders.isEmpty()) {
            throw BizException.messageOf("暂无买入的单子");
        }
        GtPreOrder maxPrice = preBuyOrders.stream()
                .sorted(Comparator.comparing(GtPreOrder::getPrice).reversed())
                .findFirst()
                .get();
        GtPreOrder minPrice = preBuyOrders.stream()
                .sorted(Comparator.comparing(GtPreOrder::getPrice))
                .findFirst()
                .get();
        GtPreOrder curQtyPreOrder = preBuyOrders.stream()
                .filter(r -> r.getMoney() <= jyQty)
                .sorted(Comparator.comparing(GtPreOrder::getPrice).reversed())
                .findFirst()
                .orElse(null);
        if (curQtyPreOrder == null) {
            throw BizException.messageOf("暂无{}的单子", jyQty);
        }
        double randMaxPrice = maxPrice.getPrice().doubleValue() - RandomUtils.nextDouble(0.1, 0.3);
        double randCurQtyPrice = curQtyPreOrder.getPrice().doubleValue() - RandomUtils.nextDouble(0.01, 0.1);
        return GtPrice.builder()
                .price(MoneyKit.of(Math.max(Math.min(randMaxPrice, randCurQtyPrice), minPrice.getPrice().doubleValue())))
                .build();
    }

    /**
     * 我要买
     */
    public void publishBuyOrder(String buyerMobile, GtPrice gtPrice, int money) {
        JSONObject resJson = this.get(buyerMobile, "http://app.gtnu.org/App/Api/Trade?act=goBuy",
                new JSONObject()
                        .fluentPut("money", money)
                        .fluentPut("price", gtPrice.getPrice())
        );
        checkResJson(resJson);
    }

    /**
     * 我要卖
     */
    public void publishSellOrder(String sellerMobile, GtPrice gtPrice, int money) {
        JSONObject resJson = this.get(sellerMobile, "http://app.gtnu.org/App/Api/Trade?act=goSale",
                new JSONObject()
                        .fluentPut("money", money)
                        .fluentPut("price", gtPrice.getPrice())
        );
        checkResJson(resJson);
    }

    /**
     * 卖给他
     */
    public void bidJy(JSONObject seller, Integer jyId) {
        final AtomicBoolean success = new AtomicBoolean(false);
        String sellerMobile = seller.getString("mobile");
        JSONObject resJson;
        // 保全!!!
        execute(() -> {
            ThreadKit.sleepSeconds(3);
            if (!success.get()) {
                notificationService.syncEmail("GT 执行补偿抢单");
                bidJyNoProxy(sellerMobile, jyId, seller);
            }
        });
        resJson = this.get(sellerMobile, "http://app.gtnu.org/App/Api/Trade?act=goSaleTrade",
                new JSONObject()
                        .fluentPut("pwd", seller.getString("payPassword"))
                        .fluentPut("id", jyId)
        );
        try {
            checkResJson(resJson);
            success.set(true);
        } catch (Exception e) {
            GtOrder gtOrder = this.getMyIngTopSellOrder(sellerMobile);
            if (gtOrder != null && System.currentTimeMillis() - gtOrder.getCreateTime().getTime() <= 2 * DateUtils.MILLIS_PER_MINUTE) {
                // 算成功
                notificationService.syncEmail("GT 抢单异常，已判断为正常，请核查");
                return;
            } else {
                throw e;
            }
        }
    }

    private JSONObject bidJyNoProxy(String sellerMobile, Integer jyId, JSONObject seller) {
        return newMyHttpRequest(sellerMobile, "http://app.gtnu.org/App/Api/Trade?act=goSaleTrade", "GET", true)
                .param("pwd", seller.getString("payPassword"))
                .param("id", jyId)
                .executeUtilReturn200()
                .asJson();
    }

    public JieTuOuterContext createJieTuOuterContext(Integer jyId) {
        JSONObject jyRow = queryForRow(jdbcTemplate, "select * from t_gt_jy where jyId = ?", jyId);
        String buyerMobile = jyRow.getString("buyerMobile");
        String sellerMobile = jyRow.getString("sellerMobile");
        JSONObject buyer = queryForRow(jdbcTemplate, "select * from t_gt_account where mobile = ?", buyerMobile);
        JSONObject seller = queryForRow(jdbcTemplate, "select * from t_gt_account where mobile = ?", sellerMobile);
        Device device = Device.valueOf(buyer.getString("device"));
        return JieTuOuterContext.builder()
                .device(device)
                .buyerMobile(buyerMobile)
                .buyerPayMethod(buyer.getString("payMethod"))
                .amount(jyRow.getBigDecimal("orderAmount"))
                .sellerRealName(seller.getString("realName"))
                .build();
    }

    private static final Map<Integer, Integer> LVL_MAX_QTYS = Maps.newHashMap();

    static {
        LVL_MAX_QTYS.put(1, 1);
        LVL_MAX_QTYS.put(2, 2);
    }

    public String findNextRegisterPromotionCode() {
        JSONObject newRegisterRow = queryForRow(jdbcTemplate, "select * from t_gt_register_promotion_code " +
                " where promotionCode not in (select registerPromotionCode from t_gt_account)" +
                " order by rand() limit 1"
        );
        if (newRegisterRow != null) {
            return newRegisterRow.getString("promotionCode");
        }
        for (int i = 0; i < 100; i++) {
            List<JSONObject> rows = queryForRows(jdbcTemplate, "select * from t_gt_account " +
                            " where idStatus = 'success' and idSuccessTime < ? " +
                            " and root in (select promotionCode from t_gt_register_promotion_code)" +
                            " order by rand() limit 10",
                    DateUtils.addMinutes(new Date(), -30)
            );
            for (JSONObject row : rows) {
                int lvl = row.getIntValue("lvl");
                int maxQty = LVL_MAX_QTYS.getOrDefault(lvl, 0);
                if (maxQty == 0) {
                    continue;
                }
                String username = row.getString("username");
                if (jdbcTemplate.queryForObject("select count(1) from t_gt_account where registerPromotionCode = ?", int.class, username) < maxQty) {
                    return username;
                }
            }
        }
        return null;
    }

    public void tryResetPayAccount(String mobile) {
        try {
            this.resetPayAccount(mobile);
        } catch (Exception e) {
            jdbcTemplate.update("update t_gt_account set mark = ? where mobile = ?", e.getMessage(), mobile);
            logger.info("Reset Pay Account Error", e);
        }
    }

    private void resetPayAccount(String mobile) {
        JSONObject row = queryForRow(jdbcTemplate, "select * from t_gt_account where mobile = ? and isMyAccount = 0", mobile);
        if (row == null) {
            return;
        }
        if (StringUtils.isBlank(row.getString("alipay"))
                || "-1".equalsIgnoreCase(row.getString("alipay"))
                || StringUtils.isNotBlank(row.getString("sellPayAccount"))) {
            this.submitBankInfo(mobile);
        }
    }

    public void syncMyAccountOrders(String mobile) {
        List<GtOrder> myOrders = getMyOrders(mobile);
        // getdetail
        for (GtOrder gtOrder : myOrders) {
            if (jdbcTemplate.queryForObject("select count(1) from t_gt_order where id = ?", int.class, gtOrder.getId()) > 0) {
                continue;
            }
            GtOrderDetail orderDetail;
            if (gtOrder.getType2() == 0) {
                // buy
                orderDetail = this.getOrderDetailAsBuyer(mobile, gtOrder.getId());
            } else {
                // sell
                orderDetail = this.getOrderDetailAsSeller(mobile, gtOrder.getId());
            }
            JSONObject row = new JSONObject()
                    .fluentPut("id", gtOrder.getId())
                    .fluentPut("mobile", mobile)
                    .fluentPut("type", gtOrder.getType2())
                    .fluentPut("jyAmount", orderDetail.getObj().getSaleMoney())
                    .fluentPut("buyerMobile", orderDetail.getObj().getBuyMemberPhone())
                    .fluentPut("buyerUsername", orderDetail.getObj().getBuyMemberName())
                    .fluentPut("buyerFullName", orderDetail.getObj().getBuyMemberFullName())
                    .fluentPut("sellerMobile", orderDetail.getObj().getSaleMemberPhone())
                    .fluentPut("sellerUsername", orderDetail.getObj().getSaleMemberName())
                    .fluentPut("sellerFullName", orderDetail.getObj().getSaleMemberFullName())
                    .fluentPut("orderTime", orderDetail.getObj().getCreateTime());
            insertRow(jdbcTemplate, "t_gt_order", row);
        }
    }

    protected boolean isMyMobile(String mobile) {
        return jdbcTemplate.queryForObject("select count(1) from v_gt_my_account where mobile = ?", int.class, mobile) > 0;
    }


    public void setRoots() {
        List<JSONObject> rows = queryForRows(jdbcTemplate, "select mobile, registerPromotionCode from t_gt_account where root is null");
        for (JSONObject row : rows) {
            String root = getRoot(row.getString("registerPromotionCode"));
            jdbcTemplate.update("update t_gt_account set root = ? where mobile = ?", root, row.getString("mobile"));
        }
    }

    private String getRoot(String registerPromotionCode) {
        JSONObject parentRow = queryForRow(jdbcTemplate, "select * from t_gt_account where username = ?", registerPromotionCode);
        if (parentRow == null) {
            return registerPromotionCode;
        }
        String root = parentRow.getString("root");
        if (StringUtils.isNotBlank(root)) {
            return root;
        }
        return getRoot(parentRow.getString("registerPromotionCode"));
    }

    public void once() {
        this.newToMyJyWithAutoSelectSeller("liuzheqiang", "ManualJy", "15300496210");
    }

    protected JSONObject post(String mobile, String url) {
        return this.post(mobile, url, new JSONObject());
    }

    protected JSONObject post(String mobile, String url, JSONObject params) {
        return this.request(mobile, url, "POST", params);
    }

    protected JSONObject get(String mobile, String url) {
        return this.get(mobile, url, new JSONObject());
    }

    protected JSONObject get(String mobile, String url, JSONObject params) {
        return this.request(mobile, url, "GET", params);
    }

    protected MyHttpRequest newMyHttpRequest(String mobile, String url, String method) {
        return this.newMyHttpRequest(mobile, url, method, false);
    }

    // 用于含有文件上传的
    protected MyHttpRequest newMyHttpRequest(String mobile, String url, String method, boolean noProxy) {
        JSONObject row = queryForRow(jdbcTemplate, "select * from t_gt_account where mobile = ?", mobile);
        if (row == null) {
            throw BizException.messageOf("{} 在系统中不存在", mobile);
        }
        String cookies = row.getString("cookies");
        CookieStore cookieStore = getCookieStore(mobile, cookies);
        HttpClient httpClient;
        if (noProxy || url.contains("mylist") || url.contains("goBuy")) {
            httpClient = newHttpClient(mobile, cookieStore);
        } else {
            httpClient = newHttpClientWithMyProxy(mobile, cookieStore);
        }
        String token = StringUtils.EMPTY;
        if (StringUtils.isNotBlank(row.getString("token"))) {
            token = row.getString("token");
        }
        String targetUrl;
        if (url.contains("?")) {
            targetUrl = url + "&token=" + token;
        } else {
            targetUrl = url + "?token=" + token;
        }
        MyHttpRequest myHttpRequest;
        if ("GET".equalsIgnoreCase(method)) {
            myHttpRequest = MyHttpRequest.get(httpClient, targetUrl);
        } else {
            myHttpRequest = MyHttpRequest.post(httpClient, targetUrl);
        }
        return myHttpRequest;
    }

    protected JSONObject request(String mobile, String url, String method, JSONObject params) {
        Date updateTime = jdbcTemplate.queryForObject("select updateTime from t_gt_account where mobile = ?", Date.class, mobile);
        if (System.currentTimeMillis() - updateTime.getTime() >= 3 * DateUtils.MILLIS_PER_HOUR
                && !url.contains(CheckTokenUrl)) {
            doCheckToken(mobile);
        }
        MyHttpRequest myHttpRequest = this.newMyHttpRequest(mobile, url, method);
        myHttpRequest.params(params);
        JSONObject resJson = null;
        for (int i = 0; i < 5; i++) {
            resJson = myHttpRequest
                    .executeUtilReturn200()
                    .asJson();
            String code = resJson.getString("code");
            // 代理的问题
            if (StringUtils.isNotBlank(code)) {
                continue;
            }
            break;
        }
        String message = resJson.getString("Message");
        if (StringUtils.isNotBlank(message) && message.contains("登录已失效")) {
            doLogin(mobile);
            return request(mobile, url, method, params);
        }
        CookieStore cookieStore = getCookieStore(mobile);
        if (cookieStore != null) {
            jdbcTemplate.update("update t_gt_account set cookies = ? where mobile = ?", Requests.toCookieString(cookieStore), mobile);
        }
        return resJson;
    }

    public boolean isNotJyTime() {
        return !isJyTime();
    }

    public boolean isJyTime() {
        String jyTime = tryGetProperties(String.class, "9-21", "Gt_Jy_Time");
        return TimeUtils.isNowMatched(jyTime);
    }

    @Override
    protected HttpClientBuilder newHttpClientBuilder(CookieStore cookieStore) {
        return super.newHttpClientBuilder(cookieStore)
                .setUserAgent("Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1");
    }
}
